from app.db import db  # 从独立的 db 模块导入
from app.Models.FinanceModels.ExpenseSecondCate import ExpenseSecondCate  # 按实际路径调整
from sqlalchemy.exc import IntegrityError


class ExpenseSecondCateDAO:
    """支出二级分类模型的数据访问对象"""

    @staticmethod
    def create_second_cate(second_cate_name, first_cate_id):
        """
        新增支出二级分类
        :param second_cate_name: 二级分类名称（如"早餐"、"公交"）
        :param first_cate_id: 所属一级分类ID
        :return: 新增的二级分类对象
        :raises ValueError: 名称为空、一级分类无效或名称重复时抛出
        """
        # 1. 基础校验：名称非空处理
        clean_name = second_cate_name.strip() if second_cate_name else ""
        if not clean_name:
            raise ValueError("二级分类名称不能为空")

        # 2. 校验同属一级分类下的名称唯一性
        existing = ExpenseSecondCateDAO.get_by_name_and_first(clean_name, first_cate_id)
        if existing:
            raise ValueError(f"一级分类下已存在该二级分类：{clean_name}")

        # 3. 执行新增操作
        try:
            new_cate = ExpenseSecondCate(
                second_cate_name=clean_name,
                first_cate_id=first_cate_id
            )
            db.session.add(new_cate)
            db.session.commit()
            return new_cate
        except IntegrityError:
            db.session.rollback()
            raise ValueError("创建失败：一级分类不存在或名称重复")
        except Exception as e:
            db.session.rollback()
            raise ValueError(f"数据库错误：{str(e)}")

    @staticmethod
    def get_second_cate_by_id(cate_id):
        """
        根据ID查询二级分类
        :param cate_id: 二级分类ID
        :return: 二级分类对象或None
        """
        return ExpenseSecondCate.query.get(cate_id)

    @staticmethod
    def get_by_name_and_first(second_name, first_id):
        """
        按名称和一级分类ID查询（用于同级别名称去重校验）
        :param second_name: 二级分类名称
        :param first_id: 一级分类ID
        :return: 二级分类对象或None
        """
        if not (second_name and first_id):
            return None
        return ExpenseSecondCate.query.filter_by(
            second_cate_name=second_name.strip(),
            first_cate_id=first_id
        ).first()

    @staticmethod
    def get_seconds_by_first_cate(first_cate_id):
        """
        查询指定一级分类下的所有二级分类
        :param first_cate_id: 一级分类ID
        :return: 二级分类列表（空列表表示无数据）
        """
        return ExpenseSecondCate.query.filter_by(
            first_cate_id=first_cate_id
        ).order_by(ExpenseSecondCate.id.asc()).all()

    @staticmethod
    def get_all_second_cates():
        """
        查询所有二级分类（按一级分类ID和自身ID排序）
        :return: 所有二级分类列表
        """
        return ExpenseSecondCate.query.order_by(
            ExpenseSecondCate.first_cate_id.asc(),
            ExpenseSecondCate.id.asc()
        ).all()

    @staticmethod
    def update_second_cate(cate_id, new_name=None, new_first_id=None):
        """
        更新二级分类（支持修改名称和所属一级分类）
        :param cate_id: 二级分类ID
        :param new_name: 新名称（可选）
        :param new_first_id: 新一级分类ID（可选）
        :return: 更新后的二级分类对象
        :raises ValueError: 分类不存在、参数无效或名称重复时抛出
        """
        # 1. 校验分类是否存在
        cate = ExpenseSecondCateDAO.get_second_cate_by_id(cate_id)
        if not cate:
            raise ValueError(f"二级分类不存在（ID：{cate_id}）")

        # 2. 处理名称更新
        if new_name is not None:
            clean_new_name = new_name.strip()
            if not clean_new_name:
                raise ValueError("二级分类名称不能为空")

            # 确定目标一级分类ID（使用新ID或原ID）
            target_first_id = new_first_id or cate.first_cate_id
            # 校验新名称在目标分类下的唯一性
            existing = ExpenseSecondCateDAO.get_by_name_and_first(clean_new_name, target_first_id)
            if existing and existing.id != cate_id:
                raise ValueError(f"目标一级分类下已存在名称：{clean_new_name}")

            cate.second_cate_name = clean_new_name

        # 3. 处理所属一级分类更新
        if new_first_id is not None and new_first_id != cate.first_cate_id:
            cate.first_cate_id = new_first_id

        # 4. 执行更新
        try:
            db.session.commit()
            return cate
        except IntegrityError:
            db.session.rollback()
            raise ValueError("更新失败：一级分类不存在或名称重复")
        except Exception as e:
            db.session.rollback()
            raise ValueError(f"数据库错误：{str(e)}")

    @staticmethod
    def delete_second_cate(cate_id, check_expenses=False, expense_dao=None):
        """
        删除二级分类
        :param cate_id: 二级分类ID
        :param check_expenses: 是否校验该分类是否被支出记录引用
        :param expense_dao: 支出记录DAO（check_expenses=True时必填）
        :return: True（删除成功）
        :raises ValueError: 分类不存在、被引用或删除失败时抛出
        """
        # 1. 校验分类是否存在
        cate = ExpenseSecondCateDAO.get_second_cate_by_id(cate_id)
        if not cate:
            raise ValueError(f"二级分类不存在（ID：{cate_id}）")

        # 2. 可选：校验是否被支出记录引用
        if check_expenses:
            if not expense_dao:
                raise ValueError("校验支出引用时需传入expense_dao")
            related_expenses = expense_dao.get_expenses_by_second_cate(cate_id)
            if related_expenses:
                raise ValueError(f"无法删除：该分类被{len(related_expenses)}条支出记录引用")

        # 3. 执行删除
        try:
            db.session.delete(cate)
            db.session.commit()
            return True
        except Exception as e:
            db.session.rollback()
            raise ValueError(f"删除失败：{str(e)}")
